package function

import (
	"reflect"

	"gitee.com/knowgo/ego/core/data"
	"gitee.com/knowgo/ego/core/inject"
	"gitee.com/knowgo/ego/core/validator"
	"gitee.com/knowgo/ego/egoc"
)

type ServiceLoaderField struct {
	Injector *inject.Injector
	// 是否可选注入
	Optional bool
	// IsPtr Feild 是否指针
	IsPtr bool
	Field string
}

type ServiceLoader struct {
	Type reflect.Type
	// struct 参数是否指针
	IsPtr  bool
	Fields []*ServiceLoaderField
}

func (s *ServiceLoader) Value(request *data.ServiceRequest, ctx *egoc.Context, argCtx *ArgContext) (reflect.Value, error) {
	var loader = reflect.New(s.Type)
	var elem = loader.Elem()
	// var err error
	for _, f := range s.Fields {
		var fd = elem.FieldByName(f.Field)
		v, ok := argCtx.InjectValueMap[f.Injector.Type]
		if !ok {
			val, err := f.Injector.Create(ctx)
			v = &InjectValue{Value: val, Err: err}
			argCtx.InjectValueMap[f.Injector.Type] = v
			if err != nil && !f.Optional {
				// 不是可选的注入项
				return val, err
			}
			// ctx.InjectValueMap[f.Injector.Type] = v
		}
		if v.Err == nil {
			if f.IsPtr {
				// 字段是指针类型
				fd.Set(v.Value)
			} else {
				fd.Set(v.Value.Elem())
			}
		}
	}
	if s.IsPtr {
		return loader, nil
	}
	return loader.Elem(), nil
}

func ParseServiceLoader(typ reflect.Type, typIsPtr bool, injectors *inject.InjectorManager) *ServiceLoader {
	loader := &ServiceLoader{
		Type:  typ,
		IsPtr: typIsPtr,
	}
	for n := 0; n < loader.Type.NumField(); n++ {
		field := loader.Type.Field(n)
		var fieldType = field.Type
		var fieldIsPtr = false
		if fieldType.Kind() == reflect.Ptr {
			fieldIsPtr = true
			fieldType = fieldType.Elem()
		}

		injector := injectors.Find(fieldType)
		if injector == nil {
			// 没有注入函数，忽略此字段
			continue
		}
		// 解析 optional tag
		var tag = validator.ParseTag(&field, func(tagItem string) {})
		loader.Fields = append(loader.Fields, &ServiceLoaderField{
			IsPtr:    fieldIsPtr,
			Optional: tag.Optional,
			Field:    field.Name,
			Injector: injector,
		})
	}
	return loader
}
